|
Writing a property editor
When you select a component in the designer, its properties are displayed in the object inspector. You can create your own editor for any property. The “Font” property's standard editor can exemplify that: if this property is selected, the ... button appears in the right part of the line; call the standard "font properties" dialogue box by clicking the button. One more example is the “Color” property's editor. It shows names of standard colors and color specimens in the drop-down list. The base class for all property editors is described in the “frxDsgnIntf” unit: TfrxPropertyEditor = class (TObject) protected function GetFloatValue: Extended; function GetOrdValue: Integer; function GetStrValue: String ; function GetVarValue: Variant; procedure SetFloatValue(Value: Extended); procedure SetOrdValue(Value: Integer); procedure SetStrValue( const Value: String ); procedure SetVarValue(Value: Variant); public destructor Destroy; override ; function Edit: Boolean; virtual ; function GetAttributes: TfrxPropertyAttributes; virtual ; function GetExtraLBSize: Integer; virtual ; function GetValue: String; virtual ; procedure GetValues; virtual ; procedure SetValue( const Value: String ); virtual ; procedure OnDrawLBItem(Control: TWinControl; Index: Integer; ARect: TRect; State: TOwnerDrawState); virtual ; procedure OnDrawItem(Canvas: TCanvas; ARect: TRect); virtual ; property Component: TPersistent readonly; property frComponent: TfrxComponent readonly; property Designer: TfrxCustomDesigner readonly; property ItemHeight: Integer; property PropInfo: PPropInfo readonly; property Value: String ; property Values: TStrings readonly; end; You also can inherit from any of the following classes which themselves realize some basic functionality for working with properties of corresponding types: TfrxIntegerProperty = class (TfrxPropertyEditor) TfrxFloatProperty = class (TfrxPropertyEditor) TfrxCharProperty = class (TfrxPropertyEditor) TfrxStringProperty = class (TfrxPropertyEditor) TfrxEnumProperty = class (TfrxPropertyEditor) TfrxClassProperty = class (TfrxPropertyEditor) TfrxComponentProperty = class (TfrxPropertyEditor) Several properties are defined in the class: - Component - link to the parent component (not to the property itself!), to which the given property belongs; - frComponent - the same, but casted to the TfrxComponent type (for convenience of use in some cases); - Designer – the link to the report's designer; - ItemHeight - height of the item, in which the property is displayed. It can be useful in the OnDrawXXX; - PropInfo - link to the PPropInfo structure, which contains information about the edited property; - Value - property's value displayed as a string; - Values - the list of values. This property is to be filled in the “GetValue” method, if the “paValueList” attribute is defined (see below). The following methods are service ones. They can be used to get or set a value of the edited property. function GetFloatValue: Extended; function GetOrdValue: Integer; function GetStrValue: String ; function GetVarValue: Variant; procedure SetFloatValue(Value: Extended); procedure SetOrdValue(Value: Integer); procedure SetStrValue( const Value: String ); procedure SetVarValue(Value: Variant); You should use the methods, which correspond to the property's type. Thus, use the “GetOrdValue” and the “SetOrdValue” methods, if a property is of the “Integer” type. These methods are also used for working with a property of the “TObject" type, since such property contains the 32-bit object's address. In this case, it is sufficient to do a cast of the following type, for example: MyFont := TFont(GetOrdValue). To create your own editor, it is necessary to inherit from the basic class and override one or several methods defined in the public section (this depends on the property type and functionality you wish to realize). One of the methods you surely have to override is the “GetAttributes” method. The “GetAttributes” method is to return a set of the property's attributes. The attributes are defined in the following way: TfrxPropertyAttribute = (paValueList, paSortList, paDialog, paMultiSelect, paSubProperties, paReadOnly, paOwnerDraw); TfrxPropertyAttributes = set of TfrxPropertyAttribute; Assignment of the attribute is realized as following: - paValueList - the property represents the dropping down list of values. (This function is exemplified in the “Color” property). If this attribute is presented, the “GetValues” method should be overridden; - paSortList - sorts the list's elements. It is used together with paValueList; - paDialog - the property has an editor. If this attribute is presented, the ... button is displayed in the right part of the editing line. The Edit method is called on by clicking on it; - paMultiSelect - allow editing of the given property in some objects selected at the same time. Some properties (such as “Name”, etc) do not have this attribute; - paSubProperties - the property is an object of the TPersistent type and has its own properties, which are also should be displayed. (This function is exemplified in the “Font” property); - paReadOnly - it is impossible to modify a value in the editor line. Some properties, being the “Class” or “Set” types, possess this attribute; - paOwnerDraw - drawing of the property's value is performed via the “OnDrawItem” method. If the “paValueList” attribute is defined, then drawing of the drop-down list is performed via the OnDrawLBItem method. The Edit method is called in two cases: either by selecting a property, by doubleclicking its value, or (if a property has the paDialog attribute) by clicking the ... button. The method should return “True,” if the property's value was modified. The “GetValue” method should return the property's value as a string (it will be displayed in the object inspector). If you inherit from the TfrxPropertyEditor basic class, it is necessary to override the method. The “SetValue” method is to set the property's value transferred as a string. If you inherit from the TfrxPropertyEditor basic class, it is necessary to override the method. The “GetValues” method should be overridden in case you defined the “paValueList” attribute. This method should fill the “Values” property with values. The following three methods allow performing manual drawing of the property's value (the Color property's editor works in the same way). These methods are called, if you define the “paOwnerDraw” attribute. The “OnDrawItem” method is called when drawing the property's value in the object inspector (when the property is not selected; otherwise its value is simply displayed in the editing line). For example, the Color property's editor draws a rectangle, filled with the color according to the value, at the left of the property's value. The “GetExtraLBSize” method is called in case you defined the “paValueList” attribute. The method returns the number of pixels, by which the width of the “Drop- Down List” should be adjusted in order to find room for the displayed picture. By default, this method returns the value corresponding to the cell's height for property's enveloping. If you need to deduce a picture, width of which is larger than its height, the given method should be overridden. The “OnDrawLBItem” method is called when drawing a string in the drop-down list, if you defined the paValueList attribute. In fact, this method is the TListBox.OnDrawItem event's handler and has the same set of parameters. Registration of the property's editor is performed via the procedure described in the frxDsgnIntf file: procedure frxPropertyEditors.Register(PropertyType: PTypeInfo; ComponentClass: TClass; const PropertyName: String ; EditorClass: TfrxPropertyEditorClass); - PropertyType - information about the property's type, transferred via the “TypeInfo” system function, for example TypeInfo(String); - ComponentClass - name of the component, the property of which you want to edit (may be nil); - PropertyName - name of the property you want to edit (may be a blank string); - EditorClass - name of the property's editor. It is necessary to specify the “PropertyType” parameter only. The “ComponentClass” and/or “PropertyName” parameters may be blank. This allows to register the editor either to any property of the PropertyType type, to any property of the concrete ComponentClass components and its successors, or to the PropertyName concrete property of the concrete component (or any component, if the ComponentClass parameter is blank). Let us examine three examples of the properties' editors. The editor's code, according to the agreement accepted in FastReport, can be placed in a file possessing the same name as the file with the code of the very component, adding the Editor suffix. -------------------------------------------------- { the TFont property's editor displays the editor's button(...) } { inherit from the ClassProperty } type public function GetAttributes: TfrxPropertyAttributes; override ; end; function TfrxFontProperty.GetAttributes: TfrxPropertyAttributes; begin Result := [paMultiSelect, paDialog, paSubProperties, paReadOnly]; end; function TfrxFontProperty.Edit: Boolean; var begin FontDialog := TFontDialog.Create(Application); try FontDialog.Font := TFont(GetOrdValue); FontDialog.Options := FontDialog.Options + [fdForceFontExist]; { display a dialogue } Result := FontDialog.Execute; { bind a new value } if Result then finally end; end; { registration } frxPropertyEditors.Register(TypeInfo(TFont), nil , '', TfrxFontProperty); -------------------------------------------------- { the TFont.Name property's editor displays the drop-down list of available fonts } { inherit from the StringProperty, as the property is of the string type } type public procedure GetValues; override ; end; function TfrxFontNameProperty.GetAttributes: TfrxPropertyAttributes; begin end; procedure TfrxFontNameProperty.GetValues; begin end; { registration } frxPropertyEditors.Register(TypeInfo(String), TFont, 'Name', TfrxFontNameProperty); -------------------------------------------------- { the TPen.Style. property's editor displays a picture, which is a pattern of the selected style } type public function GetExtraLBSize: Integer; override ; procedure OnDrawLBItem(Control: TWinControl; Index: Integer; ARect: TRect; State: TOwnerDrawState); override ; procedure OnDrawItem(Canvas: TCanvas; ARect: TRect); override ; end; function TfrxPenStyleProperty.GetAttributes: TfrxPropertyAttributes; begin end; { the method draws a thick horizontal line with the selected style } procedure HLine(Canvas: TCanvas; X, Y, DX: Integer); var begin begin for i := 0 to 1 do begin LineTo(X + DX, Y - 1 + i); end; end; end; { drawing the drop-down list } procedure TfrxPenStyleProperty.OnDrawLBItem(Control: TWinControl; Index: Integer; ARect: TRect; State: TOwnerDrawState); begin begin TextOut(ARect.Left + 40, ARect.Top + 1, TListBox(Control).Items [Index]); Pen.Color := clGray; Brush.Color := clWhite; Rectangle(ARect.Left + 2, ARect.Top + 2, ARect.Left + 36, ARect.Bottom - 2); Pen.Style := TPenStyle(Index); HLine(TListBox(Control).Canvas, ARect.Left + 3, ARect.Top + (ARect.Bottom - ARect.Top) div 2, 32); Pen.Style := psSolid; end; end; { drawing the property value } procedure TfrxPenStyleProperty.OnDrawItem(Canvas: TCanvas; ARect: TRect); begin begin Pen.Color := clGray; Brush.Color := clWhite; Rectangle(ARect.Left, ARect.Top + 1, ARect.Left + 34, ARect.Bottom - 4); Pen.Color := clBlack; Pen.Style := TPenStyle(GetOrdValue); HLine(Canvas, ARect.Left + 1, ARect.Top + (ARect.Bottom - ARect.Top) div 2 - 1, 32); Pen.Style := psSolid; end; end; { return the picture's width } function TfrxPenStyleProperty.GetExtraLBSize: Integer; begin end; { registration } frxPropertyEditors.Register(TypeInfo(TPenStyle), TPen, 'Style', TfrxPenStyleProperty); |